﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using LaunchPad.Core.Controls.Tracks.CanvasVirtualization;
using LaunchPad.Core.Dialogs;

namespace LaunchPad.Core.Controls.Tracks
{
    public partial class WaveViewerControl : IScrollInfo
    {
        private const double ScrollingBuffer = 40;
        private readonly MapZoom _mapZoom;
        private readonly PlaybackControl _playbackControl = new PlaybackControl();

        public WaveViewerControl()
        {
            InitializeComponent();
            PositionChanged += WaveViewerControlPositionChanged;
            SizeChanged += WaveViewerControlSizeChanged;
            _playbackControl.PositionChanged += PlaybackControlPositionChanged;
            track.SmallScrollIncrement = new Size(50, 50);
            track.UnmanagedChildren.Add(_playbackControl);
            _mapZoom = new MapZoom(track.ContentCanvas);
            scroller.ScrollChanged += ScrollerScrollChanged;
            ResetPlayback();
        }

        #region IScrollInfo Members

        public bool CanHorizontallyScroll
        {
            get { return true; }
            set { throw new NotImplementedException(); }
        }

        public bool CanVerticallyScroll
        {
            get { return false; }
            set { throw new NotImplementedException(); }
        }

        public double ExtentHeight
        {
            get { return ScrollOwner.ExtentHeight; }
        }

        public double ExtentWidth
        {
            get { return ScrollOwner.ExtentWidth; }
        }

        public double HorizontalOffset
        {
            get { return ScrollOwner.HorizontalOffset; }
        }

        public void LineDown()
        {
            throw new NotImplementedException();
        }

        public void LineLeft()
        {
            throw new NotImplementedException();
        }

        public void LineRight()
        {
            throw new NotImplementedException();
        }

        public void LineUp()
        {
            throw new NotImplementedException();
        }

        public Rect MakeVisible(Visual visual, Rect rectangle)
        {
            throw new NotImplementedException();
        }

        public void MouseWheelDown()
        {
            throw new NotImplementedException();
        }

        public void MouseWheelLeft()
        {
            throw new NotImplementedException();
        }

        public void MouseWheelRight()
        {
            throw new NotImplementedException();
        }

        public void MouseWheelUp()
        {
            throw new NotImplementedException();
        }

        public void PageDown()
        {
            ScrollOwner.PageDown();
        }

        public void PageLeft()
        {
            ScrollOwner.PageLeft();
        }

        public void PageRight()
        {
            ScrollOwner.PageRight();
        }

        public void PageUp()
        {
            ScrollOwner.PageUp();
        }

        public ScrollViewer ScrollOwner
        {
            get { return scroller; }
            set { throw new NotImplementedException(); }
        }

        public void SetHorizontalOffset(double offset)
        {
            ScrollOwner.ScrollToHorizontalOffset(offset);
        }

        public void SetVerticalOffset(double offset)
        {
            ScrollOwner.ScrollToVerticalOffset(offset);
        }

        public double VerticalOffset
        {
            get { return ScrollOwner.VerticalOffset; }
        }

        public double ViewportHeight
        {
            get { return ScrollOwner.ViewportHeight; }
        }

        public double ViewportWidth
        {
            get { return ScrollOwner.ViewportWidth; }
        }

        #endregion

        private void ScrollerScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (ScrollChanged != null)
                ScrollChanged(this, e);
        }

        public event ScrollChangedEventHandler ScrollChanged;

        private void WaveViewerControlSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (e.HeightChanged)
                _playbackControl.Height = e.NewSize.Height;
        }

        private void WaveViewerControlPositionChanged(object sender, PlaybackPositionEventArgs e)
        {
            // we need to adjust by the zoom factor here since it gets applied by the virtual canvas as well
            _playbackControl.Position = e.Position/ZoomFactor;

            if (_playbackControl.Position - scroller.HorizontalOffset > scroller.ViewportWidth - ScrollingBuffer)
                scroller.ScrollToHorizontalOffset(_playbackControl.Position + ScrollingBuffer - scroller.ViewportWidth);
        }

        private void PlaybackControlPositionChanged(object sender, EventArgs e)
        {
            // we need to adjust by the zoom factor here since it gets applied by the virtual canvas as well
            PlaybackPosition = TimePositionConverter.GetTime(_playbackControl.Position*ZoomFactor);
        }

        protected override void OnTrackChanged(DependencyPropertyChangedEventArgs e)
        {
            if (AudioTrack == null)
            {
                track.VirtualChildren.Clear();
                return;
            }

            var parentWindow = Window.GetWindow(this);
            var progressDialog = new ProgressDialog
                                     {
                                         Title = "Loading Audio...",
                                         WindowStartupLocation = WindowStartupLocation.CenterScreen,
                                         Owner = parentWindow
                                     };
            progressDialog.Show();

            var progressCallbackAction = (Action<double>)(value =>
                Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => progressDialog.Value = value)));
            var progressCallback = new WorkProgressCallback(progressCallbackAction);

            var snippetProvider = new VirtualSampleProvider(AudioTrack);
            double widthPerSecond = TimePositionConverter.GetPosition(TimeSpan.FromSeconds(1));

            Task.Factory.StartNew(() => UpdateSnippetsAsync(snippetProvider, widthPerSecond, progressCallback, progressDialog));

            AudioTrack.Stopped += TrackStopped;
            ResetPlayback();
        }

        private void UpdateSnippetsAsync(VirtualSampleProvider snippetProvider, double widthPerSecond, WorkProgressCallback progressCallback, ProgressDialog progressDialog)
        {
            List<VirtualSnippet> snippets = snippetProvider.GetSnippets(widthPerSecond, progressCallback).ToList();

            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                                   (Action)(() =>
                                   {
                                       track.VirtualChildren.Clear();
                                       foreach (VirtualSnippet snippet in snippets)
                                           track.AddVirtualChild(snippet);
                                       progressDialog.Close();
                                       track.RebuildVisuals();
                                   }));
        }

        private void TrackStopped(object sender, EventArgs e)
        {
            ResetPlayback();
        }

        private void ResetPlayback()
        {
            Dispatcher.BeginInvoke((Action) delegate
                                                {
                                                    _playbackControl.Position = 0;
                                                    scroller.ScrollToHome();
                                                });
        }

        protected override void OnZoomFactorChanged(DependencyPropertyChangedEventArgs e)
        {
            _mapZoom.ZoomX = ZoomFactor;
        }

        private void ScrollerMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (IsAudioLoaded)
            {
                var position = Mouse.GetPosition(track).X + scroller.HorizontalOffset;
                var time = ConvertFromPosition(position);
                if (time < AudioTrack.Length)
                {
                    PlaybackPosition = ConvertFromPosition(Mouse.GetPosition(track).X + scroller.HorizontalOffset);
                }
            }
        }
    }
}